package com.woniu.service.impl;

import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.woniu.dto.RegisDto;
import com.woniu.entity.PatientInfo;
import com.woniu.entity.Registration;
import com.woniu.entity.Staff;
import com.woniu.entity.TimeSlot;
import com.woniu.mapper.PatientInfoMapper;
import com.woniu.mapper.RegistrationMapper;
import com.woniu.mapper.StaffMapper;
import com.woniu.mapper.TimeSlotMapper;
import com.woniu.publisher.AddRegistrationPublisher;
import com.woniu.publisher.UpdateRegistrationPublisher;
import com.woniu.service.RegistrationService;
import com.woniu.utils.ConstantUtil;
import com.woniu.utils.ResponseEnum;
import com.woniu.utils.ResponseUtil;
import com.woniu.utils.SnowflakeIdGenerator;
import com.woniu.vo.RegistrationVO;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.temporal.ChronoUnit;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

@Service
public class RegistrationServiceImpl implements RegistrationService {
    @Resource
    private RegistrationMapper registrationMapper;
//    @Override
//    public ResponseUtil findAll() {
//        List<RegistrationVO> all = registrationMapper.findAll();
//        return !all.isEmpty() ? ResponseUtil.get(ResponseEnum.OK,all)
//                : ResponseUtil.get(ResponseEnum.FAIL);
//    }

    @Override
    public ResponseUtil findAll(RegisDto regisDto) {
        Integer pageNum = regisDto.getPageNum();
        Integer pageSize = regisDto.getPageSize();
        PageHelper.startPage(pageNum,pageSize);


        List<Registration> all = registrationMapper.findAll(regisDto);
        System.out.println(all);
        PageInfo<Registration> registrationPageInfo = new PageInfo<>(all);
//        System.out.println( all);
        return !all.isEmpty() ? ResponseUtil.get(ResponseEnum.OK,registrationPageInfo)
                : ResponseUtil.get(ResponseEnum.FAIL,"查无此人");
    }

    @Override
    public ResponseUtil findByNo(String rsNo) {
        RegistrationVO registration = registrationMapper.findByNo(rsNo);
        return registration!=null ? ResponseUtil.get(ResponseEnum.OK,registration)
                : ResponseUtil.get(ResponseEnum.FAIL,"无此订单");
    }
    @Transactional(rollbackFor = Exception.class)
    @Override
    public ResponseUtil tuiHao(String rsNo) {
        if (rsNo == null || rsNo.trim().isEmpty()) {
            return ResponseUtil.get(ResponseEnum.PARAM_ERROR, "缺少必要参数");
        }

        Registration registration = registrationMapper.findByRsNo(rsNo);
        if (registration == null) {
            return ResponseUtil.get(ResponseEnum.FAIL, "挂号记录不存在");
        }

        Integer status = registration.getRsStatus();
        Date preTimeDate = registration.getRsPreTime(); // 原始类型是 java.util.Date

        if (preTimeDate == null) {
            return ResponseUtil.get(ResponseEnum.FAIL, "预约时间无效");
        }

        // 将 Date 转换为 LocalDateTime（使用系统默认时区，通常为 UTC+8）
        LocalDateTime preTime = preTimeDate.toInstant()
                .atZone(ZoneId.systemDefault())
                .toLocalDateTime();

        LocalDateTime now = LocalDateTime.now();

        // 状态 0：待缴费 → 允许随时退号
        if (Objects.equals(status, 0)) {
            // 继续执行
        }
        // 状态 1：待就诊 → 必须至少提前 2 小时
        else if (Objects.equals(status, 1)) {
            long hoursBetween = ChronoUnit.HOURS.between(now, preTime);
            if (hoursBetween < 2) {
                return ResponseUtil.get(ResponseEnum.FAIL, "退号需至少提前2小时，当前已不满足条件");
            }
        }
        // 其他状态禁止退号
        else {
            return ResponseUtil.get(ResponseEnum.FAIL, "此订单无法退号");
        }

        // ========== 原有退号逻辑保持不变 ==========

        // 1. 释放号源
        TimeSlot timeSlot = timeSlotMapper.selectByPrimaryKey(registration.getTimeSlotId());
        if (timeSlot == null) {
            return ResponseUtil.get(ResponseEnum.FAIL, "排班信息不存在");
        }

        if (timeSlot.getCurrentNum() > 0) {
            timeSlot.setCurrentNum(timeSlot.getCurrentNum() - 1);
        }
        int slotUpdateCount = timeSlotMapper.updateByPrimaryKeySelective(timeSlot);
        if (slotUpdateCount <= 0) {
            return ResponseUtil.get(ResponseEnum.FAIL, "号源释放失败");
        }

        // 2. 如果是状态 1（已缴费），退还费用
        if (Objects.equals(status, 1)) {
            PatientInfo patientInfo = patientInfoMapper.findById(registration.getRsPatientId());
            Staff staff = staffMapper.selectByPrimaryKey(registration.getDoctorId());

            if (patientInfo == null || staff == null) {
                return ResponseUtil.get(ResponseEnum.FAIL, "患者或医生信息异常");
            }

            BigDecimal newBalance = patientInfo.getBalance().add(staff.getPrice());
            patientInfo.setBalance(newBalance);

            int balanceUpdateCount = patientInfoMapper.updateByPrimaryKeySelective(patientInfo);
            if (balanceUpdateCount <= 0) {
                return ResponseUtil.get(ResponseEnum.FAIL, "退款失败，请稍后重试");
            }
        }

        // 3. 更新状态为已退号
        registration.setRsStatus(2);
        int regUpdateCount = registrationMapper.updateByPrimaryKeySelective(registration);

        // 4. 清除缓存
        redisTemplateInit.delete("registration:" + rsNo);

        if (regUpdateCount > 0) {
            return ResponseUtil.get(ResponseEnum.OK, "退号成功");
        } else {
            return ResponseUtil.get(ResponseEnum.FAIL, "退号失败");
        }

    }

    @Resource
    private TimeSlotMapper timeSlotMapper;
    @Resource
    private StaffMapper staffMapper;
    @Resource
    private PatientInfoMapper patientInfoMapper;
    @Override
    public ResponseUtil add(Registration registration) throws InterruptedException {
        if (registration.getTimeSlotId() == null ||
                registration.getDoctorId() == null ||
                registration.getRsPatientId() == null) {
            return ResponseUtil.get(ResponseEnum.PARAM_ERROR, "缺少必要参数");
        }
        TimeSlot timeSlot = timeSlotMapper.selectByPrimaryKey(registration.getTimeSlotId());
        if (timeSlot == null) {
            return ResponseUtil.get(ResponseEnum.FAIL, "无此时段");
        }
        Staff staff = staffMapper.selectByPrimaryKey(registration.getDoctorId());
        if (staff == null) {
            return ResponseUtil.get(ResponseEnum.FAIL, "无此医生");
        }
        PatientInfo patientInfo = patientInfoMapper.findById(registration.getRsPatientId());
        if (patientInfo == null) {
            return ResponseUtil.get(ResponseEnum.FAIL, "无此用户");
        }
        // 防止同一用户对同一时段重复挂号
        String lockKey = ConstantUtil.REDIS_LOCK + registration.getTimeSlotId();
        RLock lock = redissonClient.getLock(lockKey);

        try {
            // 等待1秒，持锁5秒（防止重复提交）
            boolean flag = lock.tryLock(1, 5, TimeUnit.SECONDS);
            if (!flag) {
                return ResponseUtil.get(ResponseEnum.FAIL, "请勿重复挂号");
            }
            if (timeSlot.getCurrentNum()>=timeSlot.getMaxNum()) {
                return ResponseUtil.get(ResponseEnum.FAIL, "该时段号已抢完");
            }
            SnowflakeIdGenerator sfg = new SnowflakeIdGenerator();
            String rsNo = String.valueOf(sfg.nextId());
            Date now = new Date();
            registration.setRsNo(rsNo);
            registration.setRsDepartmentId(staff.getMedicalRoomId());
            registration.setRsCreateDate(now);
            registration.setRsCreateTime(now);
            registration.setRsStatus(0);
            registration.setRsUserId(patientInfo.getUserId());
            registration.setRsPreTime(registration.getRsPreTime());
            // 发送消息（持久化和名额控制由消费者处理）
            addRegistrationPublisher.send(registration, ConstantUtil.ROUTING_KEY_ORDER);
            updateRegistrationPublisher.send(rsNo, 15 * 60 * 1000); // 15分钟延迟
            redisTemplateInit.opsForValue().set("registration:" + rsNo,rsNo);
            return ResponseUtil.get(ResponseEnum.OK, rsNo);

        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            return ResponseUtil.get(ResponseEnum.FAIL, "操作被中断");
        } catch (Exception e) {
            e.printStackTrace();
            return ResponseUtil.get(ResponseEnum.FAIL, "挂号失败");
        } finally {
            if (lock.isLocked() && lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }

    @Resource
    private AddRegistrationPublisher addRegistrationPublisher;
    @Resource
    private RedisTemplate redisTemplateInit;
    @Resource
    private UpdateRegistrationPublisher updateRegistrationPublisher;
    @Resource
    private RedissonClient redissonClient;
    //缴费
    @Override
    public ResponseUtil pay(String rsNo) {
        Registration registration = registrationMapper.findByRsNo(rsNo);
        if(registration == null){
            return ResponseUtil.get(ResponseEnum.FAIL,"无此订单");
        }
        PatientInfo patientInfo = patientInfoMapper.findById(registration.getRsPatientId());
        Staff staff = staffMapper.selectByPrimaryKey(registration.getDoctorId());
        if(registration.getRsStatus() ==0 && patientInfo.getBalance().compareTo(staff.getPrice()) >= 0){
            patientInfo.setBalance(patientInfo.getBalance().subtract(staff.getPrice()));
            Integer i1 = patientInfoMapper.updateByPrimaryKey(patientInfo);
            if(i1 <= 0){
                return ResponseUtil.get(ResponseEnum.FAIL);
            }
            redisTemplateInit.delete("registration:" + rsNo);
            registration.setRsStatus(1);
            Integer i = registrationMapper.updateByPrimaryKey(registration);
            return i>0 ? ResponseUtil.get(ResponseEnum.OK)
                    : ResponseUtil.get(ResponseEnum.FAIL);
        }
        return ResponseUtil.get(ResponseEnum.FAIL,"无此订单");
    }

    @Override
    public ResponseUtil findByPatientId(Integer patientId) {
        List<RegistrationVO> list = registrationMapper.findByPatientId(patientId);
        return !list.isEmpty()?ResponseUtil.get(ResponseEnum.OK,list)
                :ResponseUtil.get(ResponseEnum.FAIL);
    }


}
